package com.code.user.service.impl;

import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.code.common.exception.ServiceException;
import com.code.common.model.ResponseCode;
import com.code.common.utils.HelperUtil;
import com.code.common.utils.IpUtil;
import com.code.user.btv.bo.AccountBO;
import com.code.user.constant.AccountTypeEnum;
import com.code.user.constant.LoginTypeEnum;
import com.code.user.mapstruct.AccountMap;
import com.code.user.model.AccountDO;
import com.code.user.mapper.AccountMapper;
import com.code.user.model.RoleDO;
import com.code.user.model.UserDO;
import com.code.user.service.AccountService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.code.user.service.RoleService;
import com.code.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.HttpRequestMethodNotSupportedException;

import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 账号表 服务实现类
 * </p>
 *
 * @author Yan
 * @since 2023-04-14
 */
@Slf4j
@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, AccountDO> implements AccountService, UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private UserService userService;
    @Autowired
    @Lazy
    private TokenEndpoint tokenEndpoint;
    @Autowired
    private RoleService roleService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        AccountDO account = this.searchAccountByUsername(username);
        if (StrUtil.isEmptyIfStr(account)) {
            throw new ServiceException(ResponseCode.USER_ACCOUNT_NOT_EXIST);
        }

        UserDO userInfo = userService.getOne(new LambdaQueryWrapper<UserDO>().eq(UserDO::getAccountId, account.getId()));
        List<RoleDO> roleList = roleService.searchRoleByUid(userInfo.getId());

        AccountBO accountBO = AccountMap.INSTANCE.doToBO(account);
        accountBO.setUserInfo(userInfo);
        accountBO.setRoles(roleList);
        return accountBO;
    }

    @Override
    public ResponseEntity<OAuth2AccessToken> login(Principal principal) throws HttpRequestMethodNotSupportedException {
        HttpServletRequest request = HelperUtil.getHttpServletRequest();
        String loginType = request.getParameter("loginType");
        Map<String, String> parameters = null;

        if (StringUtils.equals(LoginTypeEnum.SMS.getType(), loginType)) {
            parameters = this.buildSmsLoginMap(request);
        }else if (StringUtils.equals(LoginTypeEnum.CAPTCHA.getType(), loginType)) {
            parameters = this.buildCaptchaLoginMap(request);
        }

        return tokenEndpoint.postAccessToken(principal, parameters);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void registerAccount(String username) {
        AccountDO accountDO = new AccountDO();
        accountDO.setUsername(username);
        accountDO.setPassword(passwordEncoder.encode(IdUtil.simpleUUID()));
        accountDO.setSource(AccountTypeEnum.PHONE.getType());
        accountDO.setIpAddress(IpUtil.getIpAddress());
        this.save(accountDO);
        this.saveUser(accountDO);
    }

    private void saveUser(AccountDO account) {
        UserDO userDO = new UserDO();
        userDO.setAccountId(account.getId());
        userDO.setNickname(DesensitizedUtil.mobilePhone(account.getUsername()));
        userService.save(userDO);
        roleService.bindNormalRoleByUserId(userDO.getId());
    }

    @Override
    public AccountDO searchAccountByUsername(String username) {
        if (StringUtils.isBlank(username)) {
            return null;
        }
        LambdaQueryWrapper<AccountDO> wrapper = new LambdaQueryWrapper<AccountDO>().eq(AccountDO::getUsername, username);
        return this.baseMapper.selectOne(wrapper);
    }

    private Map<String, String> buildSmsLoginMap(HttpServletRequest request) {
        Map<String, String> parameters = new HashMap<>();
        parameters.put("grant_type", "sms");
        parameters.put("scope", "all");
        parameters.put("username", request.getParameter("username"));
        parameters.put("smsCode", request.getParameter("smsCode"));
        return parameters;
    }

    private Map<String, String> buildCaptchaLoginMap(HttpServletRequest request){
        Map<String, String> parameters = new HashMap<>();
        parameters.put("grant_type", "captcha");
        parameters.put("scope", "all");
        parameters.put("username", request.getParameter("username"));
        parameters.put("password", request.getParameter("password"));
        parameters.put("captchaCode", request.getParameter("captchaCode"));
        return parameters;
    }
}
